/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import { BaseElement, element } from '../../BaseElement';
import { resizeCanvas } from '../helper';

@element('lit-doughnut-chart')
export class LitDoughnutChart extends BaseElement {
  canvas: HTMLCanvasElement | undefined | null;
  context: CanvasRenderingContext2D | undefined | null;
  title: string = '';
  scale: number = 0.7;
  top: number = 1;
  chartData: Array<any> = [];
  width: number = 0;
  height: number = 0;
  bottom: number = 0;
  legendRows: Array<any> = [];
  centerX: number = 0;
  centerY: number = 0;
  selectIndex: number = 0;
  radius: number = 0;
  startAngle: number = -0.5;
  size: number = 500;

  initElements(): void {
    this.canvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#can');
    this.context = this.canvas!.getContext('2d');
    this.canvas!.addEventListener('click', (e) => {
      this.clickHandler(e);
    });
    new ResizeObserver((entries, observer) => {
      entries.forEach((it) => {
        this.init();
        this.drawChart();
      });
    }).observe(this);
  }

  connectedCallback() {}

  set config(config: DoughnutConfig) {
    this.title = config.title;
    this.selectIndex = config.selectIndex;
    this.chartData = config.data;
    this.init();
    this.drawChart();
  }

  clickHandler(e: MouseEvent) {
    let [x, y] = [e.offsetX, e.offsetY];
    let lineLength = Math.sqrt((this.centerX - x) * (this.centerX - x) + (this.centerX - y) * (this.centerX - y));
    let angle = Math.atan(Math.abs(this.centerX - x) / Math.abs(this.centerY - y));
    let eventAngle;
    if (lineLength < this.radius) {
      if (x > this.centerX) {
        if (y < this.centerY) {
          eventAngle = angle;
        } else {
          eventAngle = Math.PI - angle;
        }
      } else {
        if (y < this.centerY) {
          eventAngle = Math.PI * 2 - angle;
        } else {
          eventAngle = Math.PI + angle;
        }
      }
      eventAngle += this.startAngle * Math.PI;
      for (let i = 0; i < this.chartData.length; i++) {
        let item = this.chartData[i];
        if (item.startAngle < eventAngle && item.endAngle > eventAngle) {
          this.reDraw(i);
          this.dispatchEvent(
            new CustomEvent('select', {
              detail: {
                value: item,
              },
            })
          );
          return;
        }
      }
    }
    let dataCount = 0;
    for (let rowIndex = 0; rowIndex < this.legendRows.length; rowIndex++) {
      let row = this.legendRows[rowIndex];
      if (y < row.y + 5 * this.scale && y > row.y - 5 * this.scale) {
        for (let itemIndex = 0; itemIndex < row.data.length; itemIndex++) {
          let item = row.data[itemIndex];
          dataCount++;
          if (x > item.startX && x < item.startX + item.width) {
            this.reDraw(dataCount - 1);
            this.dispatchEvent(
                  new CustomEvent('select', {
                      detail: {
                          value: item,
                      },
                  })
            );
            return;
          }
        }
        return;
      }
      dataCount += row.data.length;
    }
  }

  reDraw(dataIndex: number) {
    this.context!.clearRect(0, 0, this.width, this.height);
    this.selectIndex = dataIndex;
    this.drawChart();
  }

  init() {
    resizeCanvas(this.canvas!);
    this.width = this.canvas!.clientWidth;
    this.height = this.canvas!.clientHeight;
    this.size = this.width > this.height ? this.height : this.width;
    if (this.size <= 289) {
      this.scale = 0.75;
    } else if (this.size >= 580) {
      this.scale = 1.5;
    } else {
      this.scale = this.size / 415;
    }
    this.top = 70 * this.scale;
  }

  drawChart() {
    this.drawChartTitle();
    if (this.chartData === undefined || this.chartData === null || this.chartData.length === 0) {
      this.drawNoData();
    }
    this.initLegend();
    this.drawPie();
    this.drawLegend();
  }

  drawChartTitle() {
    this.context!.font = `${18 * this.scale}px PingFangSC-Regular`;
    this.context!.fillStyle = '#000000';
    this.context!.textBaseline = 'top';
    this.context!.textAlign = 'center';
    this.context!.fillText(this.title, this.width / 2, this.scale * 5, this.width);
  }

  initLegend() {
    let xIconWidth = 20 * this.scale;
    let gapWidth = 10 * this.scale;
    let legendRows: Array<any> = [];
    let legends: Array<any> = [];
    let legendW = 0;
    this.context!.font = `${10 * this.scale}px PingFangSC-Regular`;
    this.context!.textAlign = 'left';
    this.context!.textBaseline = 'middle';
    this.chartData.map((item, i) => {
      let xTagWidth = this.context!.measureText(item.name).width + gapWidth;
      let itemWidth = xTagWidth + xIconWidth + gapWidth;
      if (legendW + itemWidth > this.width) {
        legendRows.push({
          data: legends,
          startX: (this.width - legendW) / 2 + gapWidth / 2,
        });
        legends = [];
        legendW = 0;
      }
      legendW += itemWidth;
      item.width = itemWidth
      legends.push(item);
    });
    legendRows.push({
      data: legends,
      startX: (this.width - legendW) / 2 + gapWidth / 2,
    });
    this.legendRows = legendRows;
    this.bottom = this.size - 20 * this.scale * this.legendRows.length - 40 * this.scale;
    this.centerX = this.width / 2;
    this.centerY = (this.bottom - this.top) / 2 + this.top;
    this.radius = (this.bottom - this.top) / 2;
    this.centerY = (this.bottom - this.top) / 2 + this.top;
    legendRows.map((row, i) => {
      row.y = this.bottom + 40 * this.scale + 20 * this.scale * i;
    });
  }

  drawLegend() {
    let xIconWidth = 20 * this.scale;
    this.legendRows.map((row) => {
      let startX = row.startX;
      row.data.map((item: { startX: number; name: string; color: string; width: number }) => {
        item.startX = startX;
        this.context!.beginPath();
        this.context!.font = `${10 * this.scale}px PingFangSC-Regular`;
        this.context!.textAlign = 'left';
        this.context!.textBaseline = 'middle';
        this.context!.fillStyle = '#333';
        this.context!.beginPath();
        this.context!.fillText(item.name, startX + xIconWidth, row.y);
        this.context!.fill();
        this.context!.lineWidth = this.scale;
        this.context!.fillStyle = item.color;
        this.context!.beginPath();
        this.context!.arc(startX, row.y, 5 * this.scale, 0, Math.PI * 2, false);
        this.context!.fill();
        startX += item.width;
      });
    });
  }

  initHtml(): string {
    return `
        <style>
        :host {
            width: 100%;
            height: 100%;
        }
        </style>
        <canvas id="can"></canvas>
        `;
  }

  drawNoData() {
    if (this.radius < 0) {
      return;
    }
    this.context!.fillStyle = '#D8D8D8';
    this.context!.beginPath();
    this.context!.arc(this.centerX, this.centerY, this.radius, 0, Math.PI * 2);
    this.context!.fill();
    this.context!.fillStyle = '#FFFFFF';
    this.context!.beginPath();
    this.context!.arc(this.centerX, this.centerY, this.radius * 0.55, 0, Math.PI * 2);
    this.context!.fill();
    this.context!.fillStyle = '#333333';
    this.context!.font = `${this.radius * 0.15}px PingFangSC-Regular`;
    this.context!.textBaseline = 'bottom';
    this.context!.textAlign = 'center';
    this.context!.fillText('未查询到数据', this.centerX, this.centerY);
  }

  drawPie() {
    if (this.chartData.length <= 0 || this.radius < 0) {
      return;
    }
    let drawRadius = this.radius;
    this.context!.lineWidth = drawRadius * 0.4;
    drawRadius = (this.bottom - this.top) / 2 - this.context!.lineWidth / 2;
    let totalCount = 0;
    this.chartData.map((item) => {
      totalCount += item.num;
    });
    let startAngle = this.startAngle * Math.PI;
    this.chartData.map((item, index) => {
      let percent = item.num / totalCount;
      let stepAngle = percent * Math.PI * 2;
      let endAngle = startAngle + stepAngle;
      item.startAngle = startAngle;
      item.endAngle = endAngle;
      item.percent = percent;
      let drawCenterX = this.centerX;
      let drawCenterY = this.centerY;
      if (index == this.selectIndex) {
        drawCenterX = this.centerX + Math.cos(stepAngle / 2 + startAngle) * drawRadius * 0.1;
        drawCenterY = this.centerY + Math.sin(stepAngle / 2 + startAngle) * drawRadius * 0.1;
      }
      this.context!.fillStyle = item.color;
      this.context!.strokeStyle = item.color;
      this.context!.beginPath();
      this.context!.arc(drawCenterX, drawCenterY, drawRadius, startAngle, endAngle, false);
      this.context!.stroke();
      startAngle = endAngle;
    });
    this.context!.fillStyle = 'rgba(255,255,255,0.6)';
    this.context!.beginPath();
    this.context!.arc(this.centerX, this.centerY, drawRadius * 0.55, 0, Math.PI * 2);
    this.context!.fill();
    this.context!.fillStyle = '#333333';
    this.context!.font = `${drawRadius * 0.3}px PingFangSC-Regular`;
    this.context!.textBaseline = 'bottom';
    this.context!.textAlign = 'center';
    this.context!.fillText(
      ((this.chartData[this.selectIndex].num / totalCount) * 100).toFixed(2) + '%',
      this.centerX,
      this.centerY
    );
    this.context!.fillStyle = '#666666';
    this.context!.font = `${drawRadius * 0.15}px PingFangSC-Regular`;
    this.context!.textBaseline = 'top';
    this.context!.fillText(
      `${this.chartData[this.selectIndex].name} / ${this.chartData[this.selectIndex].num}`,
      this.centerX,
      this.centerY + drawRadius * 0.1
    );
  }
}

export interface DoughnutConfig {
  title: string;
  selectIndex: number;
  data: Array<DoughnutData>;
}

export interface DoughnutData {
  name: string;
  num: number;
  color: string;
}
